<template>
  <div class="my-content-menu" ref="wrapEl" @mousedown.stop
    @contextmenu.prevent>
    <slot></slot>
    <div v-show="visiable" class="my-content-menu-wrap"
      :class="menuWrapClass"
      :style="{ left: menuPosition.left, top: menuPosition.top }">
      <slot name="menu" :menuList="menus">
        <span class="my-content-menu-wrap-item"
          :class="menuItemClass" v-for="item in menus"
          :key="item.command" @click="menuClick(item)">
          {{ item.text }}
        </span>
      </slot>
    </div>
  </div>
</template>

<script lang="ts">
import {
  ref,
  reactive,
  onMounted,
  onBeforeUnmount,
  defineComponent,
  PropType,
  nextTick,
} from "vue";
import { emitter } from "../../../../utils/emitter";
interface MenuItem {
  command: string;
  text: string;
}

type Menus = Array<MenuItem>;

export default defineComponent({
  name: "MyContextMenu",
  props: {
    menus: {
      type: Array as PropType<Menus>,
      default: () => {
        return [];
      },
      required: true,
    },
    menuWrapClass: String,
    menuItemClass: String,
  },
  emits: ["menu-click", "before-menu-click"],
  setup(props, { emit }) {
    const wrapEl = ref();
    const visiable = ref(false);
    const menuPosition: any = reactive({
      left: 0,
      top: 0,
    });

    const showContentMenuFn = (ev: any) => {
      emitter.emit("close-context-menu");
      emit("before-menu-click");
      ev.preventDefault();
      nextTick(() => {
        const rootPosition = wrapEl.value.getBoundingClientRect();
        const x = ev.x - rootPosition.left;
        const y = ev.y - rootPosition.top;
        menuPosition.top = `${y}px`;
        menuPosition.left = `${x}px`;
        visiable.value = true;
      });
    };

    const hideContentFn = () => {
      visiable.value = false;
    };

    const menuClick = (item: MenuItem) => {
      emit("menu-click", item);
    };

    onMounted(() => {
      wrapEl.value.addEventListener("contextmenu", showContentMenuFn);
      wrapEl.value.addEventListener("click", hideContentFn);
      emitter.on("close-context-menu", () => {
        hideContentFn();
      });
      document.addEventListener("click", function (e) {
        const target = e.target as Element;
        if (target.className != "my-content-menu") {
          hideContentFn();
        }
      });
    });

    onBeforeUnmount(() => {
      wrapEl.value.removeEventListener("contextmenu", showContentMenuFn);
      wrapEl.value.removeEventListener("click", hideContentFn);
      document.body.removeEventListener("click", () => {});
    });

    return {
      wrapEl,
      visiable,
      menuPosition,
      menuClick,
    };
  },
});
</script>

<style lang="less">
.my-content-menu {
  position: relative;
  z-index: 9;
  .my-content-menu-wrap {
    display: inline-block;
    box-sizing: border-box;
    background-color: #fff;
    position: absolute;
    box-shadow: 0 1px 6px rgb(0 0 0 / 20%);
    border-color: 1px solid #eee;
    border-radius: 4px;
    padding: 5px 0;
    min-width: 115px;
    .my-content-menu-wrap-item {
      cursor: pointer;
      display: block;
      line-height: 28px;
      font-size: 14px;
      padding: 0 24px;
      text-align: left;
      color: black;
      &:hover {
        background-color: #eee;
      }
    }
  }
}
</style>
